/*
Java board game library.
Copyright (C) 2011  Deepesh Garg

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/


package d.g.boardgames ;

import java.util.* ;
import java.awt.Color;
import java.awt.Graphics ;
import java.awt.Graphics2D ;
import java.awt.Dimension ;
import java.awt.BasicStroke ;
import java.awt.event.* ;
import java.awt.Component ;
import java.awt.Image ;
import javax.swing.JPanel ;


public class BoardGUI extends JPanel {
	
	Board board ;
	List<BoardEventListener> boardEventListeners = new ArrayList<BoardEventListener>() ;
	boolean flip;
	
	BoardRenderProperties renderProperties ; 

	public BoardGUI (Board b) {
		board = b;
		BoardListener bl = new BoardListener();
		addMouseListener (bl) ;
		addMouseMotionListener(bl) ;
		renderProperties  = new DefaultBoardRenderProperties () ;
	}
	
	public Board getBoard() {
		return board;
	}


	public void setBoard(Board board) {
		this.board = board;
		this.repaint();
	}


	public void addBoardEventListener (BoardEventListener boardListener) {
		boardEventListeners.add (boardListener) ;
	}

	public void removeBoardEventListener (BoardEventListener boardListener) {
		boardEventListeners.remove (boardListener) ;
	}

	public void setBoardRenderProperties (BoardRenderProperties renderProperties) {
		if (renderProperties != null) {
			this.renderProperties = renderProperties ;
		}
	}

	public BoardRenderProperties getBoardRenderProperties () {
		return this.renderProperties ;
	}

	protected int height;
	protected int width;
	protected int originX;
	protected int originY;
	protected int cellWidth;
	protected int cellHeight;
	
	public void paintComponent (Graphics g) {
		Dimension dim = getSize() ;
		int panelHeight = (int) dim.getHeight() ;
		int panelWidth = (int) dim.getWidth() ;

		height = (int) (panelHeight - (renderProperties.getPaddingY() * panelHeight/100)) ;
		width = (int) (panelWidth - (renderProperties.getPaddingX() * panelWidth/100)) ;

		originX = (panelWidth - width) / 2 ;
		originY = (panelHeight - height) / 2 ;

		Graphics2D g2d = (Graphics2D) g ;
		g2d.setColor(g2d.getBackground());
		g2d.fillRect(0, 0, panelWidth, panelHeight);

		BasicStroke bs = new BasicStroke (3) ;
		g2d.setStroke (bs) ; 

		cellHeight = height / board.getRowCount() ;
		cellWidth = width / board.getColCount() ;
		int offsetX = (int) (cellWidth * renderProperties.getOffsetX() / 100);
		int offsetY = (int) (cellHeight * renderProperties.getOffsetY() / 100);
		
		if (renderProperties.getBackgroundImage () != null) {
			Image backImg = renderProperties.getBackgroundImage() ;
			Image scaledbackImg = backImg.getScaledInstance(width, height, Image.SCALE_FAST) ;
			g2d.drawImage (scaledbackImg, originX, originY, null) ;
		}
		
		if (renderProperties.getBottomBorder(flip) != null) {
			Image borderImg = renderProperties.getBottomBorder(flip) ;
			Image scaledbBorderImg = borderImg.getScaledInstance(width, (panelHeight - height)/2, Image.SCALE_FAST) ;
			g2d.drawImage (scaledbBorderImg, originX, height + originY, null) ;
		}
		
		if (renderProperties.getTopBorder(flip) != null) {
			Image borderImg = renderProperties.getTopBorder(flip) ;
			Image scaledbBorderImg = borderImg.getScaledInstance(width, (panelHeight - height)/2, Image.SCALE_FAST) ;
			g2d.drawImage (scaledbBorderImg, originX, 0, null) ;
		}
		
		if (renderProperties.getLeftBorder(flip) != null) {
			Image borderImg = renderProperties.getLeftBorder(flip) ;
			Image scaledbBorderImg = borderImg.getScaledInstance((panelWidth - width) / 2, height, Image.SCALE_FAST) ;
			g2d.drawImage (scaledbBorderImg, 0, originY, null) ;
		}
		
		if (renderProperties.getRightBorder(flip) != null) {
			Image borderImg = renderProperties.getRightBorder(flip) ;
			Image scaledbBorderImg = borderImg.getScaledInstance((panelWidth - width) / 2, height, Image.SCALE_FAST) ;
			g2d.drawImage (scaledbBorderImg, width + originX, originY, null) ;
		}

		bs = new BasicStroke (1) ;
		g2d.setStroke (bs) ; 
		
		for (int row = 0; row < board.getRowCount(); row++) {
			for (int col = 0; col < board.getColCount(); col++) {
				int x, y;
				if (!flip) {
					x = (width * col) / board.getColCount() + 1 + originX ;
					y = (height * row) / board.getRowCount() + 1 + originY;
				} else {
					x = (width * (board.getColCount() - col - 1)) / board.getColCount() + 1 + originX;
					y = (height * (board.getRowCount() - row - 1)) / board.getRowCount() + 1 + originY;
				}

				Color cellColor = renderProperties.getCellColor(board, row, col);
				if (cellColor != null) {
					g2d.setColor(cellColor) ;
					g2d.fillRect(x, y, cellWidth, cellHeight) ;
				}
				Image img = renderProperties.getCellImage (board, row, col) ;
				if (img != null) {
					Image scaledImg = img.getScaledInstance(cellWidth, cellHeight, Image.SCALE_FAST) ;
					g2d.drawImage (scaledImg, x + offsetX, y + offsetY, null) ;
				}
				
				Color borderColor = renderProperties.getCellBorderColor(board, row, col) ;
				if (borderColor != null) {
					g2d.setColor(borderColor) ;
					g2d.drawRect(x+1, y+1, cellWidth - 4, cellHeight - 4) ;					
				}
			}
		}
		
		if (renderProperties.isGridOn ()) {
			bs = new BasicStroke (3) ;
			g2d.setStroke (bs) ; 
			g2d.setColor(Color.black) ;
			g2d.drawRect (originX + 1, originY + 1, width - 2, height - 2) ;
			for (int row = 1; row < board.getRowCount(); row++) {
				g2d.drawLine( 1 + originX, (height * row) / board.getRowCount() + originY, width + originX - 1, (height * row) / board.getRowCount() + originY) ;
			}

			for (int col = 1; col < board.getColCount(); col++) {
				g2d.drawLine( (width * col) / board.getColCount() + originX, 1 + originY, (width * col) / board.getColCount() + originX, height + originY - 1) ;
			}
		}

	}

	public boolean isFlip() {
		return flip;
	}

	public void setFlip(boolean flip) {
		this.flip = flip;
	}

	class BoardListener extends MouseAdapter {
		@Override
		public void mouseClicked (MouseEvent me) {
			raiseBoardEvent(me);
		}
		
		@Override
		public void mousePressed (MouseEvent me) {
			raiseBoardEvent(me);
		}
		
		@Override
		public void mouseReleased (MouseEvent me) {
			raiseBoardEvent(me);
		}
		
		@Override
		public void mouseDragged (MouseEvent me) {
		}
		
		private void raiseBoardEvent (MouseEvent me) {
			Component component = me.getComponent() ;
			Dimension dim = component.getSize() ;
			
			int panelHeight = (int) dim.getHeight() ;
			int panelWidth = (int) dim.getWidth() ;

			int height = (int) (panelHeight - (renderProperties.getPaddingY() * panelHeight/100)) ;
			int width = (int) (panelWidth - (renderProperties.getPaddingX() * panelWidth/100)) ;

			int originX = (panelWidth - width) / 2 ;
			int originY = (panelHeight - height) / 2 ;
			
			int x = me.getX() - originX;
			int y = me.getY() - originY;
			
			if (x < 0 || y < 0 || x > width || y > height) {
				return;
			}
			
			int rowCount = board.getRowCount() ;
			int colCount = board.getColCount() ;
			int row, col;
			if (!flip) {
				row = (y * rowCount) / height ;
				col = (x * colCount) / width ;
			} else {
				row = rowCount - (y * rowCount) / height -1 ;
				col = colCount - (x * colCount) / width - 1 ;
			}
			for (BoardEventListener boardEventListener: boardEventListeners) {
				boardEventListener.boardAction (board, row, col) ; 
			}
			component.repaint() ;			
		}
	}
}
